/*
 *  HarmonicSystem.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/06/12.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "HarmonicSystem.h"
#include "ChordList.h"
#include "../../sc/Common/WellTemperament.h"
#include "../../sc/AllExceptions.h"
#include <vector>

namespace sc
{
namespace hs
{

	ChordList* WellTemperamentHarmonicSystem12::twoNoteChords = NULL;

	ChordList* WellTemperamentHarmonicSystem12::threeNoteChords = NULL;

	void WellTemperamentHarmonicSystem12::CreateChords( Sound::WaveType type )
	{
		if ( twoNoteChords != NULL )
		{
			delete twoNoteChords;
			delete threeNoteChords;
		}

		twoNoteChords = new ChordList();
		threeNoteChords = new ChordList();
		std::vector< ::sc::Note::MidiNumber > notes;
		for ( int i = ::sc::Note::CMinus1; i <= ::sc::Note::G9; i++ )
		{
			notes.push_back( static_cast< ::sc::Note::MidiNumber >( i ) );
		}
		
		
		for ( int i = 0; i < ::sc::Note::MidiNumberCount; i++ )
		{
			for ( int j = i + 1; j < ::sc::Note::MidiNumberCount; j++ )
			{
				Chord twoNoteChord;
				twoNoteChord.push_back( Sound( type, static_cast< ::sc::Note::MidiNumber >( i ) ) );
				twoNoteChord.push_back( Sound( type, static_cast< ::sc::Note::MidiNumber >( j ) ) );
				twoNoteChords->push_back( twoNoteChord );
				for ( int k = j + 1; k < ::sc::Note::MidiNumberCount; k++ )
				{
					Chord threeNoteChord;
					threeNoteChord.push_back( Sound( type, static_cast< ::sc::Note::MidiNumber >( i ) ) );
					threeNoteChord.push_back( Sound( type, static_cast< ::sc::Note::MidiNumber >( j ) ) );
					threeNoteChord.push_back( Sound( type, static_cast< ::sc::Note::MidiNumber >( k ) ) );
					threeNoteChords->push_back( threeNoteChord );
				}
			}
		}
	}

	void WellTemperamentHarmonicSystem12::DisposeChords()
	{
		delete twoNoteChords;
		delete threeNoteChords;
	}

	ChordList* WellTemperamentHarmonicSystem12::GetChords( int noteCount )
	{
		if ( twoNoteChords == NULL )
		{
			CreateChords( Sound::SawWave );
		}
		
		switch ( noteCount )
		{
		case 2:
			return twoNoteChords;
		case 3:
			return threeNoteChords;
		default:
			throw InvalidParameterException( "noteCount is ristricted to 2 or 3" );
		}
	}
	
	Chord WellTemperamentHarmonicSystem12::CreateChordFromMidiNumber( Sound::WaveType type, const std::vector< Note::MidiNumber >& midiNumbers )
	{
		Chord chord;
		for ( size_t i = 0; i < midiNumbers.size(); i++ )
		{
			chord.push_back( Sound( type, midiNumbers[ i ] ) );
		}
		return chord;
	}

	std::vector< const sc::SoundElement32* > WellTemperamentHarmonicSystem12::SoundToSoundElements(
		const ::sc::hs::Sound& sound, double duration )
	{
		std::vector< const SoundElement32* > elements;
		static int phaseShiftIndex = 0;
		phaseShiftIndex++;

		std::vector< NoteInfo > noteInfos;
		const WaveFunction& function = WaveFunctionFactory::CreateXSinX( 0.5, 2 * duration );
	//	const WaveFunction& function = WaveFunctionFactory::CreateXSinX( 1., 1.0 / ( 2.0 * duration ) );
	//	const WaveFunction& function = WaveFunctionFactory::CreateLogXSinX( 1.0, 1.0 / ( 2.0 * duration ) );
	//	const WaveFunction& function = WaveFunctionFactory::CreateSinPowerOfX( 0.0, 1.0 / ( 2.0 * duration ) );
		const WaveTable& dynamicsTable = WaveTableFactory::Create( function, sound.GetDynamics() );

	//	const WaveTable& dynamicsTable = WaveTableFactory::Create( WaveTable::Type_Sine, 1.0 / ( 2.0 * duration ), 1.0 );

		if ( ( sound.GetFrequency() != -1 ) && ( sound.GetMidiNumber() == Note::MidiNumberMute ) )
		{
			noteInfos.push_back( NoteInfo( sound.GetFrequency(), duration, sound.GetDynamics(), dynamicsTable ) );
		}
		else
		{
			double frequency = sound.GetFrequency();
//			double frequency = WellTemperament::GetFrequency( sound.GetMidiNumber() );
			int count = sound.GetHarmonicsCount();
			for ( int i = 1; i <= count; i++ )
			{
				if ( sound.GetWaveType() == Sound::SingleSineWave )
				{
					noteInfos.push_back( 
						NoteInfo( 
							frequency, 
							duration, 
							sound.GetHarmonicAmplitude( 1 ) * sound.GetDynamics(), 
							dynamicsTable,
							0.5 * phaseShiftIndex ) );
					break;
				}

				double amplitude = sound.GetHarmonicAmplitude( i );
				if ( amplitude == 0 )
				{
					continue;
				}

				switch ( sound.GetWaveType() )
				{
				case Sound::SawWave:
					noteInfos.push_back( 
						NoteInfo( 
							i * frequency, 
							duration, 
							sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
							dynamicsTable, 
							0.5 * phaseShiftIndex ) );
					break;

				case Sound::SquareWave:
					if ( i % 2 == 1 )
					{
						noteInfos.push_back(
							NoteInfo(
								i * frequency, 
								duration,
								sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
								dynamicsTable,
								0.5 * phaseShiftIndex ) );
					}
					break;

				case Sound::TriangleWave:
					if ( i % 2 == 1 )
					{
						noteInfos.push_back(
							NoteInfo( 
								i * frequency, 
								duration, 
								sound.GetHarmonicAmplitude( i ) * sound.GetDynamics(), 
								dynamicsTable,
								0.5 * phaseShiftIndex ) );
					}
					break;
				}
			}
		}
		elements = SoundGenerator::Generate( noteInfos );
		return elements;
	}

	std::vector< const sc::SoundElement32* > WellTemperamentHarmonicSystem12::SoundListToSoundElements(
		const ::sc::hs::SoundList& sounds, double duration )
	{
		std::vector< const SoundElement32* > elements;
		for ( size_t i = 0; i < sounds.size(); i++ )
		{
			std::vector< const SoundElement32* > singleSoundElements;
			singleSoundElements = SoundToSoundElements( sounds[ i ], duration ); 
			for ( int j = 0; j < singleSoundElements.size(); j++ )
			{
				elements.push_back( singleSoundElements[ j ] );
			}
		}
		return elements;
	}

}
}
